﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================

using DevExpress.Utils;
using DevExpress.XtraBars;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraLayout;
using DevExpress.XtraLayout.Utils;
using DevExpress.XtraTreeList;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevExpress.Utils.Extensions;
using XCI.Core;
using XCI.Extensions;
using XCI.Windows;
using Exception = System.Exception;

namespace XCI.Helper
{
    /// <summary>
    /// Form操作类
    /// </summary>
    public static class WinFormHelper
    {
        public static readonly Regex DataTagRegex = new Regex(@"g\((.*)\),s\((.*)\)", RegexOptions.Compiled);
        public static readonly Regex UrlParamRegex = new Regex(@"(^|&)?(\w+)=([^&]+)(&|$)?", RegexOptions.Compiled);
        private static float? _dpiRatio;
        private static Size? _singleNumberSize;

        /// <summary>
        /// 获取dpi缩放比例
        /// </summary>
        public static float GetDpiRatio()
        {
            if (_dpiRatio.HasValue)
            {
                return _dpiRatio.Value;
            }

            using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
            {
                _dpiRatio = graphics.DpiX / 96;
                return _dpiRatio.Value;
            }
        }

        /// <summary>
        /// 获取文字的大小
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="font">字体</param>
        public static Size CalcTextSize(string text, Font font = null)
        {
            return TextRenderer.MeasureText(text, font ?? WindowsFormsSettings.DefaultFont);
        }

        /// <summary>
        /// 计算单个数字尺寸
        /// </summary>
        public static Size GetSingleNumberSize(Font font = null)
        {
            if (_singleNumberSize.HasValue) return _singleNumberSize.Value;
            _singleNumberSize = TextRenderer.MeasureText("0", font ?? WindowsFormsSettings.DefaultFont);
            return _singleNumberSize.Value;
        }

        /// <summary>
        /// 获取图片预览窗口大小
        /// </summary>
        public static Size GetImagePreviewSize()
        {
            var width = Convert.ToInt32(Math.Round(Screen.PrimaryScreen.Bounds.Width * 0.8, 0));
            var height = Convert.ToInt32(Math.Round(Screen.PrimaryScreen.Bounds.Height * 0.8, 0));

            return new Size(width, height);
            //var x = Convert.ToInt32((Screen.PrimaryScreen.Bounds.Width - width) / 2);
            //var y = Convert.ToInt32((Screen.PrimaryScreen.Bounds.Height - height) / 2);
            //return new Point(x,y);
        }

        public static void InitDesignFont()
        {
            DevExpress.Utils.AppearanceObject.DefaultFont = new Font("微软雅黑", 9.75F);
        }

        /// <summary>
        /// 获取操作系统环境变量
        /// </summary>
        /// <param name="name">变量名</param>
        public static string GetEnvironmentVariable(string name)
        {
            string env = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
            if (String.IsNullOrEmpty(env))
            {
                env = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User);
                if (String.IsNullOrEmpty(env))
                {
                    env = Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Machine);
                }
            }

            return env;
        }

        /// <summary>
        /// 递归查找控件
        /// </summary>
        /// <param name="controls">控件容器</param>
        /// <param name="name">查找的控件名称</param>
        public static Control FindControl(Control controls, string name)
        {
            if (controls.Controls.Count <= 0) return null;
            foreach (Control item in controls.Controls)
            {
                if (item.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
                {
                    return item;
                }

                if (item.Controls.Count <= 0) continue;
                Control control = FindControl(item, name);
                if (control != null)
                {
                    return control;
                }
            }

            return null;
        }

        /// <summary>
        /// 获取Radio组选中的值
        /// </summary>
        /// <param name="edits">控件数组</param>
        public static object GetRadioGroupCheckedValue(params CheckEdit[] edits)
        {
            if (edits == null || edits.Length == 0) return null;
            foreach (var item in edits)
            {
                if (item.Checked)
                {
                    return item.Tag;
                }
            }

            return null;
        }

        /// <summary>
        /// 设置Radio组的值并选中
        /// </summary>
        /// <param name="value">选中的值</param>
        /// <param name="edits">控件数组</param>
        public static void SetRadioGroupCheckedValue(object value, params CheckEdit[] edits)
        {
            if (edits == null || edits.Length == 0) return;
            foreach (var item in edits)
            {
                if (item.Tag.ToStringOrEmpty().Equals(value.ToStringOrEmpty()))
                {
                    item.Checked = true;
                    break;
                }
            }
        }

        /// <summary>
        /// 焦点导航到下一个控件或者上一个控件
        /// </summary>
        /// <param name="form">操作的表单</param>
        /// <param name="control">激活控件</param>
        /// <param name="isNext">方向 true 下一个 false 上一个控件</param>
        public static void SelectNextControl(Form form, Control control, bool isNext)
        {
            form.SelectNextControl(control, isNext, true, true, true);
        }

        /// <summary>
        /// 焦点导航到下一个控件或者上一个控件
        /// </summary>
        /// <param name="form">操作的表单</param>
        /// <param name="control">激活控件</param>
        public static void SelectNextControl(Form form, Control control)
        {
            SelectNextControl(form, control, true);
        }

        /// <summary>
        /// 焦点导航到当前激活控件的下一个控件或者上一个控件
        /// </summary>
        /// <param name="form">操作的表单</param>
        /// <param name="isNext">方向 true 下一个 false 上一个控件</param>
        public static void SelectNextControl(Form form, bool isNext)
        {
            SelectNextControl(form, form.ActiveControl, isNext);
        }

        /// <summary>
        /// 焦点导航到当前激活控件的下一个控件或者上一个控件
        /// </summary>
        /// <param name="form">操作的表单</param>
        public static void SelectNextControl(Form form)
        {
            SelectNextControl(form, form.ActiveControl, true);
        }

        /// <summary>
        /// 水平方向让控件紧跟其后
        /// </summary>
        /// <param name="first">第一个控件</param>
        /// <param name="offset">偏移量</param>
        /// <param name="comeAfterControls">其他紧跟控件</param>
        public static void ComeAfterControlHorizontal(Control first, int offset, params Control[] comeAfterControls)
        {
            var total = first.Location.X + first.Width + offset;
            for (var index = 0; index < comeAfterControls.Length; index++)
            {
                var item = comeAfterControls[index];
                item.Location = new Point(total, first.Location.Y);
                total += item.Width + offset;
            }
        }

        /// <summary>
        /// 垂直方向让控件紧跟其下
        /// </summary>
        /// <param name="first">第一个控件</param>
        /// <param name="offset">偏移量</param>
        /// <param name="comeAfterControls">其他紧跟控件</param>
        public static void ComeAfterControlVertical(Control first, int offset, params Control[] comeAfterControls)
        {
            var total = 0;
            foreach (var item in comeAfterControls)
            {
                total += item.Location.Y + item.Height + offset;
                item.Location = new Point(first.Location.X, total);
            }
        }

        /// <summary>
        /// 把控件居中显示
        /// </summary>
        /// <param name="form">操作的表单</param>
        /// <param name="centerControl">要居中的控件</param>
        /// <param name="parentControl">控件的父控件</param>
        /// <param name="isCenterWidth">是否居中宽度</param>
        /// <param name="isCenterHeight">是否居中高度</param>
        public static void CenterControl(Form form, Control centerControl, Control parentControl
            , bool isCenterWidth, bool isCenterHeight)
        {
            if (centerControl == null)
            {
                return;
            }

            if (parentControl == null)
            {
                parentControl = form;
            }

            int x = centerControl.Location.X;
            int y = centerControl.Location.Y;

            if (isCenterWidth)
            {
                x = (parentControl.Width - centerControl.Width) / 2;
            }

            if (isCenterHeight)
            {
                y = (parentControl.Height - centerControl.Height) / 2;
            }

            centerControl.Location = new Point(x, y);
        }

        /// <summary>
        /// 把控件居中显示
        /// </summary>
        /// <param name="form">操作的表单</param>
        /// <param name="centerControl">要居中的控件</param>
        /// <param name="parentControl">控件的父控件</param>
        public static void CenterControl(Form form, Control centerControl, Control parentControl)
        {
            CenterControl(form, centerControl, parentControl, true, true);
        }

        /// <summary>
        /// 居中显示窗口
        /// </summary>
        /// <param name="form">窗口</param>
        public static void CenterForm(Form form)
        {
            int x = (Screen.PrimaryScreen.WorkingArea.Width - form.Width) / 2;
            int y = (Screen.PrimaryScreen.WorkingArea.Height - form.Height) / 2;
            form.Location = new Point(x, y);
        }

        /// <summary>
        /// 设置窗口圆角
        /// </summary>
        /// <param name="width">窗口宽度</param>
        /// <param name="height">窗口高度</param>
        public static Region SetFormRegionRound(int width, int height)
        {
            Rectangle rect = new Rectangle(0, 22, width, height - 22);
            GraphicsPath path = GetRoundedRectPath(rect, 30);
            return new Region(path);
        }

        /// <summary>
        /// 获取圆角路径
        /// </summary>
        /// <param name="rect">矩形</param>
        /// <param name="radius">半径</param>
        private static GraphicsPath GetRoundedRectPath(Rectangle rect, int radius)
        {
            int diameter = radius;
            Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
            GraphicsPath path = new GraphicsPath();
            //   左上角   

            path.AddArc(arcRect, 180, 90);
            //   右上角   

            arcRect.X = rect.Right - diameter;
            path.AddArc(arcRect, 270, 90);
            //   右下角   

            arcRect.Y = rect.Bottom - diameter;
            path.AddArc(arcRect, 0, 90);
            //   左下角   

            arcRect.X = rect.Left;
            path.AddArc(arcRect, 90, 90);
            path.CloseFigure();
            return path;
        }

        /// <summary>
        /// 设置字体信息控件框和字体对话框的通讯
        /// </summary>
        /// <param name="control">字体信息控件框</param>
        /// <param name="dialog">字体对话框</param>
        public static void SetControlFont(Control control, FontDialog dialog)
        {
            if (control.Text.Length > 0)
            {
                dialog.Font = (Font)ObjectHelper.GetObjectFromString(control.Text, typeof(Font));
            }

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                control.Text = ObjectHelper.GetObjectString(dialog.Font);
            }
        }

        /// <summary>
        /// 检测控件是否包括在排除的控件中
        /// </summary>
        /// <param name="control">检测的控件</param>
        /// <param name="exceptControl">排除的控件</param>
        /// <returns>如果包含返回True</returns>
        public static bool HasBaseLayoutItemControl(BaseLayoutItem control, BaseLayoutItem[] exceptControl)
        {
            if (exceptControl != null && exceptControl.Length > 0)
            {
                return exceptControl.Any(item => item.Name.Equals(control.Name));
            }

            return false;
        }

        /// <summary>
        /// 检测控件是否包括在排除的控件中
        /// </summary>
        /// <param name="control">检测的控件</param>
        /// <param name="exceptControl">排除的控件</param>
        /// <returns>如果包含返回True</returns>
        public static bool HasControl(Control control, Control[] exceptControl)
        {
            if (exceptControl != null && exceptControl.Length > 0)
            {
                return exceptControl.Any(item => item.Name.Equals(control.Name));
            }

            return false;
        }

        /// <summary>
        /// 重置窗口大小
        /// </summary>
        /// <param name="visableControl"></param>
        /// <param name="ownerForm"></param>
        /// <param name="linkLab"></param>
        /// <param name="collapse"></param>
        /// <param name="expand"></param>
        /// <param name="collapseString"></param>
        /// <param name="expandString"></param>
        public static void ResizeFormSize(Control visableControl, Form ownerForm, LinkLabel linkLab,
            Image collapse, Image expand, string collapseString, string expandString)
        {
            if (!visableControl.Visible)
            {
                linkLab.Text = collapseString;
                linkLab.Image = collapse;
                int num = ownerForm.Height + visableControl.Height;
                ownerForm.Height = num;
            }
            else
            {
                linkLab.Text = expandString;
                linkLab.Image = expand;
                int num = ownerForm.Height - visableControl.Height;
                ownerForm.Height = num;
            }

            visableControl.Visible = !visableControl.Visible;
            CenterForm(ownerForm);
        }

        /// <summary>
        /// 快速创建窗口,显示到不可见区域,为后期启动窗口加速
        /// </summary>
        /// <typeparam name="T">窗口类型</typeparam>
        public static T InitForm<T>() where T : Form, new()
        {
            T form = new T
            {
                ShowInTaskbar = false,
                StartPosition = FormStartPosition.Manual,
                Location = new System.Drawing.Point(-100, -100),
                Size = new System.Drawing.Size(1, 1)
            };
            form.Show();
            form.Close();
            form.Dispose();
            return form;
        }

        /// <summary>
        /// 是否是设计时环境
        /// </summary>
        /// <returns>如果是设计时环境返回true</returns>
        public static bool IsDesignMode()
        {
            bool returnFlag = false;
#if DEBUG
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                returnFlag = true;
            }
            else if (Process.GetCurrentProcess().ProcessName.ToLower() == "devenv")
            {
                returnFlag = true;
            }
#endif
            return returnFlag;
        }

        /// <summary>
        /// 添加枚举下拉
        /// </summary>
        /// <param name="box">控件</param>
        /// <param name="enumType">枚举类型</param>
        /// <param name="itemDescriptionDic">项翻译字典</param>
        public static void AddEnum(ImageComboBoxEdit box, Type enumType, Dictionary<string, string> itemDescriptionDic)
        {
            box.Properties.BeginUpdate();
            try
            {
                Array values = System.Enum.GetValues(enumType);
                foreach (object obj in values)
                {
                    string name = obj.ToString();
                    string description = name;
                    if (itemDescriptionDic.ContainsKey(name))
                    {
                        description = itemDescriptionDic[name];
                    }

                    box.Properties.Items.Add(new ImageComboBoxItem(description, obj, -1));
                }
            }
            finally
            {
                box.Properties.EndUpdate();
            }
        }

        /// <summary>
        /// 添加枚举下拉
        /// </summary>
        /// <param name="enumType">枚举类型</param>
        /// <param name="boxs">控件集合</param>
        public static void AddEnum(Type enumType, params ComboBoxEdit[] boxs)
        {
            if (boxs == null || boxs.Length == 0) return;
            foreach (var edit in boxs)
            {
                edit.Properties.BeginUpdate();
                try
                {
                    Array values = Enum.GetValues(enumType);
                    foreach (var obj in values)
                    {
                        if (edit.Properties.Items.Contains(obj)) continue;
                        edit.Properties.Items.Add(obj);
                    }
                }
                finally
                {
                    edit.Properties.EndUpdate();
                }
            }
        }

        public static void FillGridViewColumn(GridView view, params ComboBoxEdit[] boxs)
        {
            if (boxs == null || boxs.Length == 0) return;
            foreach (var edit in boxs)
            {
                edit.Properties.Items.BeginUpdate();
                edit.Properties.Items.Add(string.Empty);
                view.Columns.ForEach(p =>
                {
                    if (!string.IsNullOrEmpty(p.FieldName))
                    {
                        edit.Properties.Items.Add(p.FieldName);
                    }
                });
                edit.Properties.Items.EndUpdate();
            }
        }

        ///// <summary>
        ///// 设置Bar控件字体
        ///// </summary>
        ///// <param name="barController">BarAndDockingController控件</param>
        //public static void SetBarControllerFont(BarAndDockingController barController)
        //{
        //    //if (barController==null)
        //    //{
        //    //    return;
        //    //}
        //    //var font = new Font(DevExpress.Utils.AppearanceObject.DefaultFont.Name,
        //    //    DevExpress.Utils.AppearanceObject.DefaultFont.Size - 0.5f);
        //    //barController.AppearancesBar.ItemsFont = font;

        //    //barController.AppearancesBackstageView.Button.Options.UseFont = true;
        //    //barController.AppearancesBackstageView.ButtonDisabled.Options.UseFont = true;
        //    //barController.AppearancesBackstageView.ButtonHover.Options.UseFont = true;
        //    //barController.AppearancesBackstageView.ButtonPressed.Options.UseFont = true;

        //    //barController.AppearancesBackstageView.Button.Font = font;
        //    //barController.AppearancesBackstageView.ButtonDisabled.Font = font;
        //    //barController.AppearancesBackstageView.ButtonHover.Font = font;
        //    //barController.AppearancesBackstageView.ButtonPressed.Font = font;
        //}

        public static void SetBarControllerFont(StandaloneBarDockControl standaloneBarDockControl)
        {
            standaloneBarDockControl.AutoSize = true;
        }

        public static void SetStandaloneBarDockControlSize(StandaloneBarDockControl standaloneBarDockControl)
        {
            standaloneBarDockControl.AutoSize = true;
        }

        /// <summary>
        /// 设置图标大小
        /// </summary>
        /// <param name="imageCollection">图标集合</param>
        /// <param name="isLarge">是否大图标(大图标是32,小图标是16),如果为null,取图片容器自身的宽度</param>
        public static void SetImageListSizeByDpiRatio(ImageCollection imageCollection, bool? isLarge = null)
        {
            int baseValue = 16;
            if (isLarge.HasValue && isLarge.Value) baseValue = 32;

            var ratio = GetDpiRatio();
            int size = Convert.ToInt32(baseValue * ratio);
            imageCollection.ImageSize = new System.Drawing.Size(size, size);
        }

        /// <summary>
        /// 设置控件日期格式
        /// </summary>
        /// <param name="edit">控件</param>
        /// <param name="format">格式</param>
        public static void SetDateFormat(DateEdit edit, string format)
        {
            edit.Properties.DisplayFormat.FormatType = FormatType.DateTime;
            edit.Properties.DisplayFormat.FormatString = format;

            edit.Properties.EditFormat.FormatType = FormatType.DateTime;
            edit.Properties.EditFormat.FormatString = format;

            edit.Properties.Mask.EditMask = format;
        }

        /// <summary>
        /// 设置控件日期格式
        /// </summary>
        /// <param name="edit">控件</param>
        /// <param name="format">格式</param>
        public static void SetDateFormat(BaseEdit edit, string format)
        {
            edit.Properties.DisplayFormat.FormatType = FormatType.DateTime;
            edit.Properties.DisplayFormat.FormatString = format;

            edit.Properties.EditFormat.FormatType = FormatType.DateTime;
            edit.Properties.EditFormat.FormatString = format;

            // edit.Properties.Mask.EditMask = format;
        }

        /// <summary>
        /// 设置控件日期格式
        /// </summary>
        /// <param name="edit">控件</param>
        /// <param name="hasTime">是否包含时间</param>
        public static void SetDateFormat(BaseEdit edit, bool hasTime)
        {
            SetDateFormat(edit, hasTime ? R.DEFAULT_DATETIME_PATTERN : R.DEFAULT_DATE_PATTERN);
        }

        /// <summary>
        /// 迭代指定容器中的所有控件及所有子孙控件
        /// </summary>
        /// <param name="container">容器对象</param>
        /// <param name="exec">每个控件回调</param>
        public static void ForEachContainer(object container, Action<object> exec)
        {

        }

        /// <summary>
        /// 获取Url字符串中的参数信息
        /// </summary>
        /// <param name="url">Url</param>
        /// <returns>返回Url中的参数字典</returns>
        public static SMap ParseUrlParamDic(string url)
        {
            var dic = new SMap();
            if (string.IsNullOrEmpty(url)) return dic;

            string ps = url;
            int questionMarkIndex = url.IndexOf('?');
            if (questionMarkIndex != -1)
            {
                ps = url.Substring(questionMarkIndex + 1);
            }

            // 开始分析参数对    
            var mc = UrlParamRegex.Matches(ps);
            foreach (Match m in mc)
            {
                dic.Add(m.Result("$2"), m.Result("$3"));
            }

            return dic;
        }

        /// <summary>
        /// 把宏变量插入到文本框
        /// </summary>
        /// <param name="entity">宏变量实体</param>
        /// <param name="editBox">文本框</param>
        public static void InsertMacroVariable(MacroVariableAttribute entity, TextEdit editBox)
        {
            if (editBox == null) return;
            string template = editBox.Text;
            int startIndex = editBox.SelectionStart;
            if (startIndex == 0 || startIndex > template.Length)
            {
                template = string.Concat(template, entity.Name);
            }
            else
            {
                template = template.Insert(startIndex, entity.Name);
            }

            editBox.Text = template;
        }

        /// <summary>
        /// 写日志到文本框
        /// </summary>
        /// <param name="edit">文本框</param>
        /// <param name="msg">消息</param>
        public static void WriteLogToTextBox(TextEdit edit, string msg)
        {
            WriteLogToTextBox(edit, msg, true);
        }

        /// <summary>
        /// 情况文本框中日志
        /// </summary>
        /// <param name="edit">文本框</param>
        public static void CleanLogToTextBox(TextEdit edit)
        {
            Action action = () =>
            {
                edit.Text = string.Empty;
                edit.ScrollToCaret();
            };
            if (edit.InvokeRequired)
            {
                edit.Invoke(action);
            }
            else
            {
                action();
            }
        }

        /// <summary>
        /// 写日志到文本框
        /// </summary>
        /// <param name="edit">文本框</param>
        /// <param name="msg">消息</param>
        /// <param name="isNewLine">是否换行</param>
        public static void WriteLogToTextBox(TextEdit edit, string msg, bool isNewLine)
        {
            Action action = () =>
            {
                edit.Text += msg;
                if (isNewLine)
                {
                    edit.Text += "\r\n";
                }

                edit.SelectionStart = edit.Text.Length;
                edit.ScrollToCaret();
            };
            if (edit.InvokeRequired)
            {
                edit.Invoke(action);
            }
            else
            {
                action();
            }
        }

        /// <summary>
        /// 绑定控件和对象的双向通讯(把对象的值自动赋值给控件 控件值发生变化时自动改变对象的属性值)
        /// </summary>
        /// <param name="container">控件容器</param>
        /// <param name="target">目标对象</param>
        public static void BindCustomPropertyControlValue(Control container, object target)
        {
            App.ContainerData.ForEach(container, p =>
            {
                BaseEdit current = p as BaseEdit;
                if (current == null || current.Tag.IsEmpty()) return;
                string propertyName = current.Tag.ToString().Trim();
                if (target != null)
                {
                    var pValue = ObjectHelper.GetObjectProperty(target, propertyName);
                    current.EditValue = pValue;
                }

                current.EditValueChanged += (o, e) =>
                {
                    BaseEdit edit = (BaseEdit)o;
                    if (edit.Tag == null || string.IsNullOrEmpty(edit.Tag.ToString().Trim())) return;
                    ObjectHelper.SetObjectProperty(target, edit.Tag.ToString().Trim(), edit.EditValue);
                };
            });
        }

        /// <summary>
        /// 缓存的类型
        /// </summary>
        private static readonly List<Type> CacheFormTypes = new List<Type>();

        //public static Form CreateLoginForm(params string[] args)
        //{
        //    var loginForm = AppData.LoginForm;
        //    //loginForm.Args = args;
        //    return loginForm as Form;
        //}

        //public static Form CreateMainForm(params string[] args)
        //{
        //    var mainForm = AppData.MainForm;
        //    //mainForm.Args = args;
        //    return mainForm as Form;
        //}

        /// <summary>
        /// 获取图片文件夹
        /// </summary>
        /// <param name="isLarge">是否大图标</param>
        public static string GetImageDirectory(bool isLarge)
        {
            string dir = isLarge ? "Assets\\Toolbar\\32" : "Assets\\Toolbar\\16";
            return Path.Combine(SystemDirectory.RootDirectory, dir);
        }

        public static List<Type> InitTypesCache()
        {
            if (CacheFormTypes.Count > 0) return CacheFormTypes;
            var files = new List<string>();
            var baseDir = AppDomain.CurrentDomain.BaseDirectory;
            var subs = new[] { "addIns", "lib", "extensions", "plugins", };
            files.AddRange(Directory.GetFiles(baseDir));
            foreach (var item in subs)
            {
                string path = Path.Combine(baseDir, item);
                if (Directory.Exists(path))
                {
                    files.AddRange(Directory.GetFiles(path));
                }
            }

            var vFiles = files.Where(p => p.ToLower().EndsWith(".dll")
                                          || p.ToLower().EndsWith(".exe")).ToArray();
            var ts = TypeHelper.LoadTypes(vFiles);
            CacheFormTypes.AddRange(ts);
            return CacheFormTypes;
        }

        /// <summary>
        /// 获取组件描述属性列表
        /// </summary>
        /// <typeparam name="T">组件属性类型</typeparam>
        /// <param name="predicate">检查条件</param>
        public static List<T> GetComponentCaptionProvider<T>(Func<Type, bool> predicate)
            where T : ComponentCaptionAttribute
        {
            var allTypes = InitTypesCache();
            var types = allTypes.Where(predicate);
            var list = new List<T>();
            foreach (var type in types)
            {
                var attr = ObjectHelper.GetAttribute<T>(type);
                if (attr == null || !attr.Visible) continue;

                attr.Name = type.Name;
                attr.Code = ObjectHelper.GetTypeFullName(type);
                if (!string.IsNullOrEmpty(attr.Caption))
                {
                    attr.NameSpell = SpellHelper.GetSpell(attr.Caption);
                }

                list.Add(attr);
            }

            return list;
        }

        /// <summary>
        /// 获取表格配置
        /// </summary>
        public static string GetGridViewSetting(GridView gridView)
        {
            gridView.ClearSorting();
            gridView.ClearColumnsFilter();
            gridView.ApplyFindFilter(null);
            var ms = new MemoryStream();
            gridView.SaveLayoutToStream(ms, OptionsLayoutBase.FullLayout);
            ms.Seek(0, System.IO.SeekOrigin.Begin);
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        /// <summary>
        /// 应用表格二进制配置
        /// </summary>
        public static void SetGridViewSetting(GridView gridView, string setting)
        {
            if (string.IsNullOrEmpty(setting)) return;
            var data = Encoding.UTF8.GetBytes(setting);
            MemoryStream ms = IOHelper.ToMemoryStream(data);
            if (ms == null) return;
            gridView.RestoreLayoutFromStream(ms, OptionsLayoutBase.FullLayout);
            ms.Seek(0, System.IO.SeekOrigin.Begin);
        }

        /// <summary>
        /// 获取Tree二进制配置
        /// </summary>
        public static string GetTreeSetting(TreeList tree)
        {
            tree.ClearSorting();
            tree.ClearColumnsFilter();
            tree.ApplyFindFilter(null);

            var ms = new MemoryStream();
            tree.SaveLayoutToStream(ms, OptionsLayoutBase.FullLayout);
            ms.Seek(0, System.IO.SeekOrigin.Begin);
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        /// <summary>
        /// 应用Tree二进制配置
        /// </summary>
        public static void SetTreeSetting(TreeList tree, string setting)
        {
            if (string.IsNullOrEmpty(setting)) return;
            var data = Encoding.UTF8.GetBytes(setting);
            MemoryStream ms = IOHelper.ToMemoryStream(data);
            if (ms == null) return;
            tree.RestoreLayoutFromStream(ms, OptionsLayoutBase.FullLayout);
            ms.Seek(0, System.IO.SeekOrigin.Begin);
        }

        /// <summary>
        /// 获取DevExpress控件版本
        /// </summary>
        public static string GetDevExpressVersion()
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            AssemblyName[] asmNames = asm.GetReferencedAssemblies();
            var assemblyName = asmNames.FirstOrDefault(p => p.Name.Contains("DevExpress"));
            if (assemblyName != null)
            {
                return assemblyName.Version.ToString();
            }

            return string.Empty;
        }

        /// <summary>
        /// 获取XCI框架版本
        /// </summary>
        public static string GetXCIVersion()
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            return asm.GetName().Version.ToString();
        }

        /// <summary>
        /// 获取客户端代理字符串
        /// </summary>
        public static string GetClientUserAgent()
        {
            var osName = $"Windows NT {App.ClientOsVersion}";
            var x64Name = Environment.Is64BitOperatingSystem ? "Win64; x64" : string.Empty;
            var xciName = $"WinForm/{App.ClientXCIVersion}";
            return $"Mozilla/5.0 ({osName}; {x64Name}) AppleWebKit/537.36 (KHTML, like Gecko) {xciName}";
        }


        /// <summary>
        /// 获取编辑页面标题
        /// </summary>
        /// <param name="created">是否新增</param>
        /// <param name="name">名称</param>
        public static string GetEditFormTitle(bool created, string name)
        {
            return created ? $"新建-{name}" : $"编辑-{name}";
        }

        /// <summary>
        /// 导出文件对话框
        /// </summary>
        /// <param name="owner">所在窗体</param>
        /// <param name="exportAction">导出回调函数</param>
        /// <param name="defaultFileName">默认导出文件名称</param>
        public static void ExcelExportCore(Control owner, Action<string> exportAction, string defaultFileName)
        {
            var fileSave = new SaveFileDialog
            {
                AddExtension = true,
                DefaultExt = "xlsx",
                FileName = defaultFileName,
                Filter = "Excel文件|*.xlsx"
            };
            if (fileSave.ShowDialog() == DialogResult.OK)
            {
                var loadingPanel = new LoadingMask(owner);
                try
                {
                    loadingPanel.Show();
                    exportAction?.Invoke(fileSave.FileName);
                    loadingPanel.Hide();
                    if (DialogResult.Yes == MessageBoxHelper.ShowMessageYesNo("导出成功，现在需要打开此文件吗？"))
                    {
                        try
                        {
                            Process.Start(fileSave.FileName);
                        }
                        catch (Exception e)
                        {
                            DebugHelper.CatchLogFile(e);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBoxHelper.ShowError("导出数据时发生错误:" + ex.Message);
                }
                finally
                {
                    loadingPanel.Hide();
                }
            }

            fileSave.Dispose();
        }

        public static async Task ExcelExportCore(Control owner, Func<string, Task> exportAction, string defaultFileName)
        {
            var fileSave = new SaveFileDialog
            {
                AddExtension = true,
                DefaultExt = "xlsx",
                FileName = defaultFileName,
                Filter = "Excel文件|*.xlsx"
            };
            if (fileSave.ShowDialog() == DialogResult.OK)
            {
                var loadingPanel = new LoadingMask(owner);
                try
                {
                    loadingPanel.Show();
                    await exportAction.Invoke(fileSave.FileName);
                    loadingPanel.Hide();
                    if (DialogResult.Yes == MessageBoxHelper.ShowMessageYesNo("导出成功，现在需要打开此文件吗？"))
                    {
                        try
                        {
                            Process.Start(fileSave.FileName);
                        }
                        catch (Exception e)
                        {
                            DebugHelper.CatchLogFile(e);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBoxHelper.ShowError("导出数据时发生错误:" + ex.Message);
                }
                finally
                {
                    loadingPanel.Hide();
                }
            }

            fileSave.Dispose();
        }

        public static async Task ExportExcel(Control owner, Func<string, Task> exportAction, string defaultFileName)
        {
            var fileSave = new SaveFileDialog
            {
                AddExtension = true,
                DefaultExt = "xlsx",
                FileName = defaultFileName,
                Filter = "Excel文件|*.xlsx"
            };
            if (fileSave.ShowDialog() == DialogResult.OK)
            {
                var loadingPanel = new LoadingMask(owner);
                try
                {
                    loadingPanel.Show();
                    await exportAction.Invoke(fileSave.FileName);
                    loadingPanel.Hide();
                    if (DialogResult.Yes == MessageBoxHelper.ShowMessageYesNo("导出成功，现在需要打开此文件吗？"))
                    {
                        try
                        {
                            Process.Start(fileSave.FileName);
                        }
                        catch (Exception e)
                        {
                            DebugHelper.CatchLogFile(e);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBoxHelper.ShowError("导出数据时发生错误:" + ex.Message);
                }
                finally
                {
                    loadingPanel.Hide();
                }
            }

            fileSave.Dispose();
        }

        /// <summary>
        /// 根据服务端路径，构建本地路径
        /// </summary>
        /// <param name="remotePath">根据服务端路径</param>
        /// <param name="localSubCatalog">本地目录相对目录</param>
        public static string GetLocalPathByRemotePath(string remotePath, string localSubCatalog = null)
        {
            if (remotePath.IsEmpty()) return string.Empty;

            remotePath = remotePath.Replace("/", "\\");
            if (remotePath.Substring(0, 1) == "\\")
            {
                remotePath = remotePath.Substring(1);
            }

            return string.IsNullOrEmpty(localSubCatalog)
                ? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, remotePath)
                : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, localSubCatalog, remotePath);
        }

        /// <summary>
        /// 是否是一个远程文件路径
        /// </summary>
        /// <returns>如果是返回true</returns>
        public static bool IsRemoteFilePath(string path)
        {
            if (string.IsNullOrEmpty(path)) return false;
            if (path.Substring(0, 1).Equals("/")) return true;
            return false;
        }

        /// <summary>
        /// 获取远程文件名称
        /// </summary>
        /// <param name="remoteFilePath">远程文件路径</param>
        public static string GetRemoteFileName(string remoteFilePath)
        {
            var index = remoteFilePath.LastIndexOf("/", StringComparison.Ordinal);
            if (index < 0) return remoteFilePath;
            return remoteFilePath.Substring(index + 1);
        }

        /// <summary>
        /// 指定的值是否为根节点的值
        /// </summary>
        /// <param name="nodeValue">节点的值</param>
        public static bool IsRootValue(string nodeValue)
        {
            return nodeValue.IsEmpty() || nodeValue.Equals(R.RootTreeId);
        }

        /// <summary>
        /// 如果指定的父节点值为根根节点时,设置TreeListLookUpEdit的值为空,并设置[NullText]
        /// </summary>
        /// <param name="edit">编辑器</param>
        /// <param name="parentId">父节点值</param>
        /// <param name="nullText">空文本默认:[无]</param>
        public static void SetTreeRootValue(TreeListLookUpEdit edit, string parentId, string nullText = "无")
        {
            edit.Properties.NullText = nullText;
            if (IsRootValue(parentId))
            {
                edit.EditValue = null;
            }
        }

        /// <summary>
        /// 设置布局项是否可见
        /// </summary>
        /// <param name="result">是否可见</param>
        public static LayoutVisibility LayoutItemVisible(bool result)
        {
            return result ? LayoutVisibility.Always : LayoutVisibility.Never;
        }

        /// <summary>
        /// 设置Bar按钮是否可见
        /// </summary>
        /// <param name="result">是否可见</param>
        public static BarItemVisibility BarItemVisible(bool result)
        {
            return result ? BarItemVisibility.Always : BarItemVisibility.Never;
        }

        /// <summary>
        /// 确认更新当前行状态
        /// </summary>
        public static void ConfirmUpdateRecord(GridView gridView, ChangingEventArgs e, Action action,
            string fieldName = "Status",
            string trueActionText = "启用", string falseActionText = "禁用")
        {
            if (gridView.FocusedRowHandle < 0)
            {
                return;
            }

            //var edit = gridView.ActiveEditor;
            var col = gridView.FocusedColumn;
            if (col.FieldName.Equals(fieldName))
            {
                var status = e.NewValue.ToBool();
                var actionText = status ? trueActionText : falseActionText;
                if (MessageBoxHelper.ShowWarnYesNo($"确定要[{actionText}]当前行数据吗?") == DialogResult.Yes)
                {
                    action.Invoke();
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        /// 确认更新当前行状态
        /// </summary>
        public static void ConfirmUpdateRecord(TreeList treeList, ChangingEventArgs e, Action action,
            string fieldName = "Status",
            string trueActionText = "启用", string falseActionText = "禁用")
        {
            if (treeList.GetNodeIndex(treeList.FocusedNode) < 0)
            {
                return;
            }

            var col = treeList.FocusedColumn;
            if (col.FieldName.Equals(fieldName))
            {
                var status = e.NewValue.ToBool();
                var enableText = status ? trueActionText : falseActionText;
                if (MessageBoxHelper.ShowWarnYesNo($"确定要[{enableText}]当前行数据吗?") == DialogResult.Yes)
                {
                    action.Invoke();
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        /// 获取过滤条件对象
        /// </summary>
        /// <param name="filterContainer">过滤容器</param>
        /// <param name="filter">过滤对象</param>
        /// <param name="exceptControl">排除的控件</param>
        public static object GetFilterObject(Control filterContainer, object filter, params Control[] exceptControl)
        {
            //if (filterContainer == null) throw new ArgumentNullException(nameof(filterContainer));
            //if (filter == null) throw new ArgumentNullException(nameof(filter));

            //GetFormData(filterContainer, filter, exceptControl);
            //return filter;
            return null;
        }

        /// <summary>
        /// 绑定Model对象到表单中
        /// </summary>
        /// <param name="formContainer">表单控件容器</param>
        /// <param name="model">实体对象</param>
        /// <param name="nullIsClearForm">如果model为空,是否清除</param>
        /// <param name="exceptControl">排除的控件</param>
        public static void BindFormModel(Control formContainer, object model, bool nullIsClearForm = true,
            params Control[] exceptControl)
        {
            //if (formContainer == null) throw new ArgumentNullException(nameof(formContainer));

            //if (model != null)
            //{
            //    WinFormHelper.SetFormData(formContainer, model, exceptControl);
            //}
            //else
            //{
            //    WinFormHelper.ClearForm(formContainer, exceptControl);
            //}
        }

        /// <summary>
        /// 验证数据对象
        /// </summary>
        /// <param name="model">数据对象</param>
        /// <param name="raisError">是否抛出错误信息</param>
        /// <returns>验证通过返回true</returns>
        public static bool ValidFormModel(object model, bool raisError = true)
        {
            var vresult = ValidateHelper.ValidationAs(model);
            if (!vresult.Success && raisError) MessageBoxHelper.ShowError(vresult.Msg);
            return vresult.Success;
        }

        /// <summary>
        /// 开始计算程序执行时间(默认返回:查询耗时：xx毫秒)
        /// </summary>
        /// <param name="action">执行内容</param>
        /// <returns>返回程序执行的时间</returns>
        public static string ElapsedTime(Action action)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            action();
            stopwatch.Stop();
            var ts = stopwatch.Elapsed;
            return $"耗时:{ts.TotalMilliseconds:###0.##} ms";
        }

        /// <summary>
        /// 开始计算程序执行时间(默认返回:查询耗时：xx毫秒)
        /// </summary>
        /// <param name="barItem">文本框项</param>
        /// <param name="action">执行内容</param>
        /// <returns>返回程序执行的时间</returns>
        public static void ElapsedTime(BarStaticItem barItem, Action action)
        {
            barItem.Caption = ElapsedTime(action);
        }

        /// <summary>
        /// 开始计算程序执行时间(默认返回:查询耗时：xx毫秒)
        /// </summary>
        /// <param name="barItem">文本框项</param>
        /// <param name="elaspTime">执行耗时</param>
        /// <returns>返回程序执行的时间</returns>
        public static void ElapsedTime(BarStaticItem barItem, TimeSpan elaspTime)
        {
            barItem.Caption = $"耗时:{elaspTime.TotalMilliseconds:###0.##} ms";
        }

        /// <summary>
        /// 多线程控件操作
        /// </summary>
        /// <param name="control">所属控件</param>
        /// <param name="action">操作函数</param>
        public static void ControlInvoke(Control control, Action action)
        {
            control.Invoke(new Action(action));
        }

        /// <summary>
        /// 回车键事件处理
        /// </summary>
        /// <param name="e">事件对象</param>
        /// <param name="action">处理函数</param>
        public static void EnterKeyHandle(KeyPressEventArgs e, Action action)
        {
            if (e.KeyChar == 13) //回车
            {
                action?.Invoke();
                e.Handled = true;
            }
        }

        /// <summary>
        /// 设置表单面板禁用时样式
        /// </summary>
        /// <param name="layout">表单面板</param>
        /// <param name="status">状态</param>
        public static void SetLayoutPanelDisableStatusStyle(LayoutControl layout, bool? status)
        {
            layout.Appearance.Control.ForeColor =
                layout.Root.AppearanceItemCaption.ForeColor = Color.Red;
            layout.Appearance.Control.Options.UseForeColor =
                layout.Root.AppearanceItemCaption.Options.UseForeColor = status.HasValue && !status.Value;
        }

        /// <summary>
        /// 注册Esc按键事件
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keys"></param>
        /// <param name="action"></param>
        public static bool RegOnEsc(Message msg, Keys keys, Action action)
        {
            int WM_KEYDOWN = 256;
            int WM_SYSKEYDOWN = 260;
            if (msg.Msg == WM_KEYDOWN | msg.Msg == WM_SYSKEYDOWN)
            {
                switch (keys)
                {
                    case Keys.Escape:
                        action?.Invoke();
                        break;
                }
            }

            return false;
        }


        /// <summary>
        /// 注册Esc关闭窗口事件
        /// </summary>
        /// <param name="ownerForm">所属窗口</param>
        public static void RegEscCloseForm(Form ownerForm)
        {
            ownerForm.KeyPreview = true;
            ownerForm.KeyPress += (o, p) =>
            {
                if (p.KeyChar == 27)
                {
                    ownerForm.Close();
                }
            };
        }

        /// <summary>
        /// 解析页面参数字符串为参数集合
        /// </summary>
        /// <param name="paramString">参数字符串</param>
        public static List<KeyValueCaption> ResolvingFormParam(string paramString)
        {
            if (string.IsNullOrEmpty(paramString))
            {
                return new List<KeyValueCaption>();
            }

            List<KeyValueCaption> paramList = null;
            try
            {
                if (paramString.StartsWith("[") || paramString.StartsWith("{"))
                {
                    paramList = JsonHelper.FromJsonString<List<KeyValueCaption>>(paramString);
                }
                else
                {
                    var dic = WinFormHelper.ParseUrlParamDic(paramString);
                    paramList = dic.Select(p => new KeyValueCaption(p.Key, p.Value)).ToList();
                }
            }
            catch
            {
                // ignored
            }
            finally
            {
                paramList = paramList ?? new List<KeyValueCaption>();
            }

            return paramList;
        }

        /// <summary>
        /// ConfirmAction
        /// </summary>
        public static void Confirm(string confirmMessage, Action callback)
        {
            if (MessageBoxHelper.ShowWarnYesNo(confirmMessage) != DialogResult.Yes)
            {
                return;
            }

            callback?.Invoke();
        }

        public static void ExceptionShowErrorBox(Exception e)
        {
            MessageBoxHelper.ShowError(DebugHelper.BuildAllMessage(e));
        }

        public static void BoolResultShowBox(BoolResult result, string successMsg = "操作成功", string failMsg = null)
        {
            if (result.Success)
            {
                MessageBoxHelper.ShowMessage(successMsg);
            }
            else
            {
                MessageBoxHelper.ShowError(string.IsNullOrEmpty(failMsg) ? result.Msg : failMsg);
            }
        }

        /// <summary>
        /// 转为服务端接受的拖拽列表
        /// </summary>
        public static List<TreeNodeDnd> ToTreeNodeDnds(List<PrimaryKeyValue> values)
        {
            var result = new List<TreeNodeDnd>();
            var groups = values.GroupBy(p => p.Id).Select(g => new TreeNodeDndGroup(g.Key, g.ToList()));
            foreach (var item in groups)
            {
                foreach (var kv in item.List)
                {
                    string id = kv.Id;
                    string parentId = null;
                    if (kv.Key.Equals("ParentId")) parentId = kv.Value;

                    string index = null;
                    if (kv.Key.Equals("Path")) index = kv.Value;

                    result.Add(new TreeNodeDnd(id, parentId, index));
                }
            }
            return result;
        }

        /// <summary>
        /// 获取父节点或者默认父节点
        /// </summary>
        /// <param name="parentId">父节点,如果为空返回默认节点[RootTreeId]</param>
        public static string GetParentIdOrDefault(string parentId)
        {
            return parentId.IsNotEmpty() ? parentId : R.RootTreeId;
        }

        /// <summary>
        /// 保存控件配置
        /// </summary>
        /// <param name="view">表格视图</param>
        /// <param name="xmlFile">xml文件路径</param>
        public static void SaveLayout(GridView view, string xmlFile)
        {
            view.FindFilterText = null;
            view.ClearSorting();
            view.MRUFilters.Clear();
            view.ActiveFilter.Clear();
            view.SaveLayoutToXml(xmlFile, OptionsLayoutBase.FullLayout);
        }

        /// <summary>
        /// 加载配置到控件
        /// </summary>
        /// <param name="view">表格视图</param>
        /// <param name="xmlFile">xml文件路径</param>
        public static void RestoreLayout(GridView view, string xmlFile)
        {
            view.Columns.Clear();
            view.MRUFilters.Clear();
            view.RestoreLayoutFromXml(xmlFile, OptionsLayoutBase.FullLayout);
            var dpi = WinFormHelper.GetDpiRatio();
            if (view.OptionsSelection.MultiSelect && view.OptionsSelection.MultiSelectMode == GridMultiSelectMode.CheckBoxRowSelect && view.OptionsSelection.CheckBoxSelectorColumnWidth > 0)
            {
                view.OptionsSelection.CheckBoxSelectorColumnWidth = Convert.ToInt32(view.OptionsSelection.CheckBoxSelectorColumnWidth * dpi);
            }
        }

        /// <summary>
        /// 测试服务器连接
        /// </summary>
        /// <param name="config">配置</param>
        /// <param name="loading">Load面板</param>
        /// <returns>测试连接成功返回true</returns>
        public static async Task<bool> WebApiClientTest(WebApiConfig config, LoadingMask loading = null)
        {
            if (config.BaseUrl.IsEmpty())
            {
                MessageBoxHelper.ShowWarn("请指定服务器基地址");
                return false;
            }

            try
            {
                loading?.Show();
                var client = new WebApiClient(config);
                var result = await client.PostAsBoolResultAsync("/core/ping");
                if (result != null)
                {
                    loading?.Hide();
                    MessageBoxHelper.ShowMessage("服务器连接成功");
                    return true;
                }
            }
            catch (Exception ex)
            {
                loading?.Hide();
                MessageBoxHelper.ShowError("服务器连接失败\n" + DebugHelper.BuildAllMessage(ex));
            }
            finally
            {
                loading?.Hide();
            }
            return false;
        }
    }
}